DROP FUNCTION IF EXISTS pg_catalog.run_command_on_all_nodes;

CREATE FUNCTION pg_catalog.run_command_on_all_nodes(command text, parallel bool default true, give_warning_for_connection_errors bool default false,
                                                    OUT nodeid int, OUT success bool, OUT result text)
    RETURNS SETOF record
    LANGUAGE plpgsql
    AS $function$
DECLARE
    i int;
    nodename text;
    nodenames text[];
    port int;
    ports int[];
    commands text[];
    current_node_is_in_metadata boolean;
    command_result_of_current_node text;
BEGIN
i := 0;
for nodename,port in SELECT nodename,nodeport FROM pg_dist_node
    WHERE isactive = 't' AND nodecluster = current_setting('spq.cluster_name')
    AND (
        (current_setting('spq.use_secondary_nodes') = 'never' AND noderole = 'primary')
        OR
        (current_setting('spq.use_secondary_nodes') = 'always' AND noderole = 'secondary')
    ) ORDER BY nodename, nodeport loop
    nodenames[i] = nodename;
    ports[i] = port;
    commands[i] = command;
    i := i + 1;
end loop;

SELECT count(*) > 0 FROM pg_dist_node
WHERE isactive = 't'
AND nodecluster = current_setting('spq.cluster_name')
AND groupid IN (SELECT groupid FROM pg_dist_local_group)
INTO current_node_is_in_metadata;

-- This will happen when we call this function on coordinator and
-- the coordinator is not added to the metadata.
-- We'll manually add current node to the lists to actually run on all nodes.
-- But when the coordinator is not added to metadata and this function
-- is called from a worker node, this will not be enough and we'll
-- not be able run on all nodes.
IF NOT current_node_is_in_metadata THEN
    nodenames[i] = current_setting('spq.local_hostname');
    ports[i] = current_setting('port')::int;
    commands[i] = command;
END IF;

FOR nodeid, success, result IN
    SELECT coalesce(pg_dist_node.nodeid, 0) AS nodeid, mrow.success, mrow.result
    FROM master_run_on_worker(nodenames, ports, commands, parallel) mrow
    LEFT JOIN pg_dist_node ON mrow.node_name = pg_dist_node.nodename AND mrow.node_port = pg_dist_node.nodeport
LOOP
    IF give_warning_for_connection_errors AND NOT success THEN
        RAISE WARNING 'Error on node with node id %: %', nodeid, result;
    END IF;
    RETURN NEXT;
END LOOP;
END;
$function$;
